/*	--- 非对称加密算法 --- 

从DH算法我们可以看到，公钥-私钥组成的密钥对是非常有用的加密方式，因为公钥是可以公开的，而私钥是完全保密的，由此奠定了非对称加密的基础。


非对称加密:  就是'加密'和'解密'使用的不是相同的密钥; 只有'同一个公钥-私钥对'才能正常加解密。

--------------------

因此，如果小明要加密一个文件发送给小红，他应该首先向小红索取'小红的公钥';  

然后，他用小红的公钥加密，把加密文件发送给小红，此文件只能由小红的私钥解开，因为小红的私钥在她自己手里，

所以，除了小红，没有任何人能解开此文件。


----------------------

非对称加密的典型算法就是RSA算法，它是由Ron Rivest，Adi Shamir，Leonard Adleman这三个哥们一起发明的，所以用他们仨的姓的首字母缩写表示。


非对称加密相比对称加密的显著优点在于，对称加密需要协商密钥，而非对称加密可以安全地公开各自的公钥.

在N个人之间通信的时候：使用非对称加密只需要N个密钥对，每个人只管理自己的密钥对。而使用对称加密需要则需要N*(N-1)/2个密钥，因此每个人需要管理N-1个密钥，密钥管理难度大，而且非常容易泄漏。


既然非对称加密这么好，那我们抛弃对称加密，完全使用非对称加密行不行？也不行。因为非对称加密的缺点就是运算速度非常慢，比对称加密要慢很多。

-------------------------

所以，在实际应用的时候，非对称加密总是和对称加密一起使用。

假设小明需要给小红需要传输加密文件，他俩首先交换了各自的公钥，然后：

	1. 小明生成'一个随机的AES口令'，然后小明用'小红的公钥'通过'RSA算法'加密这个口令，并发给小红；
	2. 小红用自己的RSA私钥解密得到AES口令；
	3. 双方使用这个共享的AES口令用AES加密通信。


( AES加密标准: 又称为高级加密标准Rijndael加密法，是美国国家标准技术研究所NIST旨在取代DES的21世纪的加密标准。AES的基本要求是，采用对称分组密码体制，密钥长度可以为128、192或256位，分组长度128位，算法应易在各种硬件和软件上实现。1998年NIST开始AES第一轮分析、测试和征集，共产生了15个候选算法。 )


Java标准库提供了RSA算法的实现，示例代码如下： */
import java.math.BigInteger;
import java.security.*;
import javax.crypto.Cipher;

public class Main {
	public static void main(String[] args) throws Exception {
		// 明文
		byte[] plain = "Hello, encrypt use RSA ".getBytes("UTF-8"); // plain  adj.  明显的,浅白的,坦诚地  n. 平原

		// 创建'公钥/私钥 对'
		Person alice = new Person("Alice"); // 同时生成'Public/Private KeyPair'

		// 用Alice的'公钥'加密
		byte[] pk = alice.getPublicKey();
		System.out.println(String.format("public key: %x", new BigInteger(1, pk)));

		byte[] encrypted = alice.encrypt(plain); //this.pk
		System.out.println(String.format("encrypted: %x", new BigInteger(1, encrypted)));


		// 用Alice的私钥解密
		byte[] sk = alice.getPrivateKey();
		System.out.println(String.format("private key: %x", new BigInteger(1, sk)));

		byte[] decrypted = alice.decrypt(encrypted); // this.sk
		System.out.println(new String(decrypted, "UTF-8"));
	}
}

class Person {
	String name;

	// 私钥 / 公钥
	PrivateKey sk;
	PublicKey pk;

	public Person(String name) throws GeneralSecurityException {
		this.name = name;

		// 生成 公钥/私钥对
		KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
		kpGen.initialize(1024);

		KeyPair kp = kpGen.generateKeyPair();
		this.sk = kp.getPrivate();
		this.pk = kp.getPublicKey();
	}

	// 把'私钥'导出为byte[]
	public byte[] getPrivateKey() {
		return this.sk.getEncoded();
	}

	// 把'公钥'导出为byte[]
	public byte[] getPublicKey() {
		return this.pk.getEncoded();
	}

	// 用公钥加密
	public byte[] encrypt(byte[] message) throws GeneralSecurityException {
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.ENCRYPT_MODE, this.pk);

		return cipher.doFinal(message);
	}

	// 用私钥解密
	public byte[] decrypt(byte[] input) throws GeneralSecurityException {
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.DECRYPT_MODE, this.sk);

		return cipher.doFinal(input);
	}
}



/*
RSA的'公钥'和'私钥'都可以通过getEncoded()方法获得为byte[]表示的二进制'公/私钥'数据，并根据需要保存到文件中。

要从byte[]数组恢复公钥或私钥，可以这么写： */

	byte[] pkData = ...
	byte[] skData = ...

	KeyFactory kf = KeyFactory.getInstance("RSA");

	// 恢复公钥
	X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(pkData);
	PublicKey pk = kf.generatePublic(pkSpec);

	// 恢复私钥
	PKCS8EncodedKeySpec skSpec = new PKCS8EncodedKeySpec(skData);
	PrivateKey sk = kf.generatePrivate(skSpec);


/*
以'RSA算法'为例，它的密钥有 256/512/1024/2048/4096等 不同的长度。

长度越长，密码强度越大，当然计算速度也越慢。


如果修改待加密的byte[]数据的大小，可以发现，使用512bit的RSA加密时，明文长度不能超过53字节，使用1024bit的RSA加密时，明文长度不能超过117字节.

这也是为什么使用RSA的时候，总是配合AES一起使用

即先用AES加密任意长度的明文(减少约束)，再用RSA加密AES口令。


此外，只使用非对称加密算法不能防止中间人攻击。

------------------------------------------------*/






/*	---- 非对称加密算法 の 小结 ---- 


1. '非对称加密'就是'加密'和'解密'使用的【不是相同的密钥】，只有【同一个公钥-私钥对】才能正常加解密；


2. 只使用'非对称加密算法'不能防止中间人攻击。(搭配对称加密算法使用)



*/